package com.xinyuan.plug.generator;

import com.xinyuan.plug.model.EntityInfo;
import com.xinyuan.plug.model.FieldInfo;
import com.xinyuan.plug.util.Constant;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

public class WrapperGenerator {
    private String refColumn;
    private String refTable;

    private String DEFAULT_PRIMARY_KEY = "id";
    private String DELETED_FIELD = "deleted";
    private String RELEASED_FIELD = "released";
    private String UPDATE_TIME_FIELD = "updateAt";
    private String CREATE_TIME_FIELD = "createAt";
    //数据库列名称
    private String DEFAULT_PRIMARY_KEY_COL = "id";
    private String DELETED_COL = "deleted";
    private String RELEASED_COL = "released";
    private String UPDATE_TIME_COL = "update_at";
    private String CREATE_TIME_COL = "create_at";

    private final int IS_TRUE = 1;
    private final int IS_FALSE = 0;

    public String generateWrapper(EntityInfo entity, String daoPackageName, Map<String, String> commonFields) {
        StringBuilder sb = new StringBuilder();

        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
        sb.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n");
        sb.append("<mapper namespace=\"").append(daoPackageName).append(".").append(entity.getSimpleName()).append("Dao\">\n\n");
        // Generate ref table and columns
        refTable = "table" + entity.getSimpleName();
        refColumn = "column" + entity.getSimpleName();

        // Generate commonFields
        generateCommonFields(commonFields);

        // Generate base table
        generateBaseTableName(sb, entity);
        // Generate base column list
        generateBaseColumnList(sb, entity);

        // Generate insert
        generateInsert(sb, entity);

        // Generate update
        generateUpdate(sb, entity);

        // Generate delete
        generateDelete(sb, entity);

        // Generate select
        generateSelect(sb, entity);

        sb.append("</mapper>");

        return sb.toString();
    }

    /**
     * 驼峰转下划线
     *
     * @param camelCase
     * @return
     */
    public String camelToUnderline(String camelCase) {
        String res = camelCase.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
        res = "\"" + res + "\"";  //解决属性是SQL关键字问题
        return res;
    }


    private void generateCommonFields(Map<String, String> commonFields) {
        if (commonFields == null) commonFields = new HashMap<>();
        if (StringUtils.isNotEmpty(commonFields.get(Constant.PRIMARY_KEY))) {
            DEFAULT_PRIMARY_KEY = commonFields.get(Constant.PRIMARY_KEY);
            DEFAULT_PRIMARY_KEY_COL = camelToUnderline(commonFields.get(Constant.PRIMARY_KEY));
        }
        if (StringUtils.isNotEmpty(commonFields.get(Constant.DELETED))) {
            DELETED_FIELD = commonFields.get(Constant.DELETED);
            DELETED_COL = camelToUnderline(commonFields.get(Constant.DELETED));
        }
        if (StringUtils.isNotEmpty(commonFields.get(Constant.RELEASED))) {
            RELEASED_FIELD = commonFields.get(Constant.RELEASED);
            RELEASED_COL = camelToUnderline(commonFields.get(Constant.RELEASED));
        }
        if (StringUtils.isNotEmpty(commonFields.get(Constant.UPDATE_TIME))) {
            UPDATE_TIME_FIELD = commonFields.get(Constant.UPDATE_TIME);
            UPDATE_TIME_COL = camelToUnderline(commonFields.get(Constant.UPDATE_TIME));
        }
        if (StringUtils.isNotEmpty(commonFields.get(Constant.CREATE_TIME))) {
            CREATE_TIME_FIELD = commonFields.get(Constant.CREATE_TIME);
            CREATE_TIME_COL = camelToUnderline(commonFields.get(Constant.CREATE_TIME));
        }
    }

    private void generateBaseTableName(StringBuilder sb, EntityInfo entity) {
        sb.append("    <!--  tableName  -->").append("\n");
        sb.append("    <sql id=\"").append(refTable).append("\">").append(entity.getTableName()).append("</sql>\n");
    }

    private void generateBaseColumnList(StringBuilder sb, EntityInfo entity) {
        sb.append("    <!--  columns  -->").append("\n");
        sb.append("    <sql id=\"").append(refColumn).append("\">\n");
        sb.append("        ");
        for (int i = 0; i < entity.getFields().size(); i++) {
            FieldInfo field = entity.getFields().get(i);
            sb.append("\"").append(field.getColumnName()).append("\"");   //转译列名防止与数据库关键字重名
            if (i < entity.getFields().size() - 1) {
                sb.append(", ");
            }
            if (i > 0 && i % 4 == 0 && i < entity.getFields().size() - 1) {
                sb.append("\n        ");
            }
        }
        sb.append("\n    </sql>\n\n");
    }

    private void generateInsert(StringBuilder sb, EntityInfo entity) {
        sb.append("    <!--  insert into  -->").append("\n");
        sb.append("    <insert id=\"insert\" parameterType=\"").append(entity.getClassName()).append("\">\n");
        sb.append("        INSERT INTO ").append("<include refid=\"").append(refTable).append("\" />").append(" (\n");
        sb.append("        <include refid=\"").append(refColumn).append("\" />\n");
        sb.append("        ) VALUES (\n");
        sb.append("        ");
        for (int i = 0; i < entity.getFields().size(); i++) {
            FieldInfo field = entity.getFields().get(i);
            if (field.getFieldName().equals(RELEASED_FIELD)) {
                sb.append(IS_TRUE);
            } else if (field.getFieldName().equals(DELETED_FIELD)) {
                sb.append(IS_FALSE);
            } else if (field.getFieldName().equals(UPDATE_TIME_FIELD) || field.getFieldName().equals(CREATE_TIME_FIELD)) {
                sb.append("NOW()");
            } else {
                sb.append("#{").append(field.getFieldName()).append("}");
            }
            if (i < entity.getFields().size() - 1) {
                sb.append(",");
            }
        }
        sb.append("\n");
        sb.append("        )\n");
        sb.append("    </insert>\n\n");
    }

    private void generateUpdate(StringBuilder sb, EntityInfo entity) {
        sb.append("    <!--  update  -->").append("\n");
        sb.append("    <update id=\"updateById\" parameterType=\"java.lang.Long\">\n");
        sb.append("        UPDATE ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        SET \n");
        for (int i = 0; i < entity.getFields().size(); i++) {
            FieldInfo field = entity.getFields().get(i);
            //忽略更新ID,启用禁用,是否删除,创建时间字段
            if (field.getFieldName().equals(DEFAULT_PRIMARY_KEY)) continue;
            if (field.getFieldName().equals(RELEASED_FIELD)) continue;
            if (field.getFieldName().equals(DELETED_FIELD)) continue;
            if (field.getFieldName().equals(UPDATE_TIME_FIELD)) continue;
            if (field.getFieldName().equals(CREATE_TIME_FIELD)) continue;
            sb.append("            <if test=\"").append(field.getFieldName()).append(" != null\">\n");
            sb.append("                 ").append(field.getColumnName()).append(" = #{").append(field.getFieldName()).append("},").append("\n");
            sb.append("            </if>");
            sb.append("\n");
        }
        sb.append("            ").append(UPDATE_TIME_COL).append(" = NOW() ").append("\n");
        sb.append("        WHERE ").append(DEFAULT_PRIMARY_KEY_COL).append(" = #{id} \n");
        sb.append("    </update>\n\n");

        sb.append("    <!--  enable  -->").append("\n");
        sb.append("    <update id=\"enableById\">\n");
        sb.append("        UPDATE ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        SET ").append(UPDATE_TIME_COL).append(" = NOW(),").append(RELEASED_COL).append(" = ").append("#{released}").append("\n");
        sb.append("        WHERE ").append(DEFAULT_PRIMARY_KEY_COL).append(" = #{id} \n");
        sb.append("    </update>\n\n");
    }

    private void generateDelete(StringBuilder sb, EntityInfo entity) {
        sb.append("    <!--  delete  -->").append("\n");
        sb.append("    <update id=\"deleteById\" parameterType=\"java.lang.Long\">\n");
        sb.append("        UPDATE ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        SET ").append(UPDATE_TIME_COL).append(" = NOW(),").append(DELETED_COL).append(" = ").append(IS_TRUE).append("\n");
        sb.append("        WHERE ").append(DEFAULT_PRIMARY_KEY_COL).append(" = #{id} \n");
        sb.append("    </update>\n\n");
    }

    private void generateSelect(StringBuilder sb, EntityInfo entity) {

        // selectById
        sb.append("    <!--  select by id  -->").append("\n");
        sb.append("    <select id=\"selectById\" resultType=\"").append(entity.getClassName()).append("\" parameterType=\"java.lang.Long\">\n");
        sb.append("        SELECT <include refid=\"").append(refColumn).append("\" />\n");
        sb.append("        FROM ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        WHERE ").append(DEFAULT_PRIMARY_KEY_COL).append(" = #{id}\n");
        sb.append("        AND ").append(DELETED_COL).append(" = ").append(IS_FALSE).append(" \n");
        sb.append("    </select>\n\n");

        // selectAll
        sb.append("    <!--  select all  -->").append("\n");
        sb.append("    <select id=\"selectAll\" resultType=\"").append(entity.getClassName()).append("\">\n");
        sb.append("        SELECT <include refid=\"").append(refColumn).append("\" />\n");
        sb.append("        FROM ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        WHERE ").append(DELETED_COL).append(" = ").append(IS_FALSE).append(" \n");
        sb.append("        ORDER BY ").append(CREATE_TIME_COL).append(" DESC \n");
        sb.append("    </select>\n\n");

        // selectPage
        sb.append("    <!--  select page  -->").append("\n");
        sb.append("    <select id=\"selectPage\" resultType=\"").append(entity.getClassName()).append("\">\n");
        sb.append("        SELECT <include refid=\"").append(refColumn).append("\" />\n");
        sb.append("        FROM ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        WHERE ").append(DELETED_COL).append(" = ").append(IS_FALSE).append(" \n");
        sb.append("        <if test=\"search!=null\">\n");
        sb.append("             AND \"name\" LIKE CONCAT('%', #{search}, '%')").append("\n");
        sb.append("        </if>\n");
        sb.append("        ORDER BY ").append(CREATE_TIME_COL).append(" DESC \n");
        sb.append("        OFFSET ${start} LIMIT ${length} \n");
        sb.append("    </select>\n\n");

        // selectPageCount
        sb.append("    <!--  select page  -->").append("\n");
        sb.append("    <select id=\"selectPageCount\" resultType=\"java.lang.Integer\">\n");
        sb.append("        SELECT COUNT(id) \n");
        sb.append("        FROM ").append("<include refid=\"").append(refTable).append("\" />").append("\n");
        sb.append("        WHERE ").append(DELETED_COL).append(" = ").append(IS_FALSE).append(" \n");
        sb.append("        <if test=\"search!=null\">\n");
        sb.append("             AND \"name\" LIKE CONCAT('%', #{search}, '%')").append("\n");
        sb.append("        </if>\n");
        sb.append("    </select>\n\n");
    }
}
